#include "regserver.h"
#include "processor.h"
#include "Poco/Exception.h"
#include "../client/log.h"
#include "..\client\RC4.h"

using Poco::Exception;
// Handler
RegServerHandler::RegServerHandler(StreamSocket& socket, SocketReactor& reactor):
_socket(socket),
    _reactor(reactor),
    _pBuffer(new char[BUFFER_SIZE])
{
    Application& app = Application::instance();
    app.logger().information("Connection from " + socket.peerAddress().toString());
    FLogger.information("Connection from " + socket.peerAddress().toString());

    _reactor.addEventHandler(_socket, NObserver<RegServerHandler, ReadableNotification>(*this, &RegServerHandler::onReadable));
    _reactor.addEventHandler(_socket, NObserver<RegServerHandler, ShutdownNotification>(*this, &RegServerHandler::onShutdown));
}

void RegServerHandler::onReadable(const AutoPtr<ReadableNotification>& pNf)
{
    FunctionLogger al(__FUNCTION__);
    int n = -1;

    try
    {
        n = _socket.receiveBytes(_pBuffer, BUFFER_SIZE);
    }
    catch (Exception& e)
    {
    	FLogger.error("error occurred while receiving bytes");
        FLogger.information(e.what());
        FLogger.information(e.displayText());
        _socket.close();
        n = -1;
    }
    if (n > 0)
    {
        cout << n << "bytes received...";
        FLogger.information("bytes received, processing...");

        RC4 rc;
        rc.decrypt((u_char*)_pBuffer, (u_char*)_pBuffer, SC_LEN);

        ServerResponse res = {0};
        DataProcessor::Process(_pBuffer, n, &res);
        FLogger.information("processed successfully, sending back to client...");

        u_char* puOut = (u_char*)&res;
        rc.encrypt(puOut, puOut, SR_LEN);
        try
        {
            _socket.sendBytes(&res, SR_LEN);
        }
        catch (Exception& e)
        {
        	FLogger.information("error while sending server response");
            FLogger.information(e.what());
        }
    }
    else
    {
        FLogger.information("0 bytes received, destroying...");
        delete this;
    }
}

void RegServerHandler::onShutdown(const AutoPtr<ShutdownNotification>& pNf)
{
    delete this;
}

// RegServer
RegServer::RegServer(): _helpRequested(false)
{
}

RegServer::~RegServer()
{
}

void RegServer::initialize(Application& self)
{
    loadConfiguration(); // load default configuration files, if present
    ServerApplication::initialize(self);
}

void RegServer::uninitialize()
{
    ServerApplication::uninitialize();
}

void RegServer::defineOptions(OptionSet& options)
{
    ServerApplication::defineOptions(options);

    options.addOption(
        Option("help", "h", "display help information on command line arguments")
        .required(false)
        .repeatable(false));
}

void RegServer::handleOption(const std::string& name, const std::string& value)
{
    ServerApplication::handleOption(name, value);

    if (name == "help")
        _helpRequested = true;
}

void RegServer::displayHelp()
{
    HelpFormatter helpFormatter(options());
    helpFormatter.setCommand(commandName());
    helpFormatter.setUsage("OPTIONS");
    helpFormatter.setHeader("An echo server implemented using the Reactor and Acceptor patterns.");
    helpFormatter.format(std::cout);
}

int RegServer::main(const std::vector<std::string>& args)
{
    if (_helpRequested)
    {
        displayHelp();
    }
    else
    {
        // get parameters from configuration file
        unsigned short port = 9977;

        // set-up a server socket
        ServerSocket svs(port);
        // set-up a SocketReactor...
        SocketReactor reactor;
        // ... and a SocketAcceptor
        SocketAcceptor<RegServerHandler> acceptor(svs, reactor);
        // run the reactor in its own thread so that we can wait for 
        // a termination request
        Thread thread;
        thread.start(reactor);
        // wait for CTRL-C or kill
        waitForTerminationRequest();
        // Stop the SocketReactor
        reactor.stop();
        thread.join();
    }
    return Application::EXIT_OK;
}